package com.yang.config;

import com.yang.util.ContentCachingRequestWrapper;
import com.yang.util.WeComNoticeUtil;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 全局异常拦截处理

 */
@RestControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
    private static String REQUESTBODY = "requestBodyMessage";
    @Value("${spring.application.name}")
    private String appName;
    @Value("${spring.profiles.active:}")
    private String appActive;

    /**
     * 统一处理非自定义异常外的所有异常
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(value = Exception.class)
    public String exceptionHandler(HttpServletRequest request, Exception e) {
        weComNotice(request, e);
        return "";
    }

    /**
     * 由于body在接口读取后无法获取，这里把body提前取出放到参数中，在上面处理异常时使用
     */
    @ModelAttribute
    public void getBodyInfo(HttpServletRequest request) {
        //获取requestBody
        try {
            ContentCachingRequestWrapper requestWapper = null;
            if (request instanceof HttpServletRequest) {
                requestWapper = (ContentCachingRequestWrapper) request;
            }
            String body = IOUtils.toString(requestWapper.getBody(), request.getCharacterEncoding());
            request.setAttribute(REQUESTBODY, body);
        } catch (Exception e) {

        }
    }

    /**
     * 异常通知企业微信
     * 有中文的异常默认不会发送, 这里只会发送没有预料到的异常信息
     * @param request 请求体
     * @param exception 异常信息
     */
    private void weComNotice(HttpServletRequest request, Exception exception) {
        try {
            LOGGER.error("系统发生未知异常:{}", exception.getMessage(), exception);
            String sb = "<font color=\"warning\">系统发生异常,请及时处理</font>\n" +
                    ">**[项目环境]**: {0}\n" +
                    "**[URL]**: {1}\n" +
                    "**[请求方法]**: {2}\n" +
                    "**[params请求参数]**: \n{3}\n" +
                    "**[body请求参数]**: \n{4}\n" +
                    "**[异常信息]**: {5}\n" +
                    "**[异常位置]**: \n{6}";
            // URL
            String expUrl = request.getRequestURI();
            // 系统-环境
            String environment = appName + "(" + appActive + ")" ;
            // 请求方法
            StackTraceElement stackTraceElement = exception.getStackTrace()[0];
            String expController = stackTraceElement.getClassName();
            String expMethod = stackTraceElement.getMethodName();
            String method = expController + "#" + expMethod;
            // 请求参数
            StringBuilder params = new StringBuilder();
            Map<String, String[]> parameterMap = request.getParameterMap();
            for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
                params.append(entry.getKey()).append(": ").append(Arrays.toString(entry.getValue())).append("\n");
            }
            if (params.length() != 0){
                params.deleteCharAt(params.length() - 1);
            }
            Object body = request.getAttribute(REQUESTBODY);
            String bodyString = body != null ? body.toString() : "";
            // 错误信息
            String message = exception.getMessage();
            // 错误位置
            StringBuilder point = new StringBuilder();
            for (StackTraceElement st : exception.getStackTrace()) {
                if (st.getLineNumber() > 0) {
                    point.append("at: ").append(st.getClassName());
                    point.append("#").append(st.getMethodName());
                    point.append("[line:").append(st.getLineNumber()).append("]\n");
                }
            }
            // 判断不含有中文的才转发
            Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
            Matcher m = p.matcher(message == null ? "null" : message);
            if (m.find()) {
                return;
            }
            String exceptionMsg = MessageFormat.format(sb, environment, expUrl, method, params.toString(), bodyString, message, point.toString());
            WeComNoticeUtil.send(exceptionMsg);
        }catch (Exception e){
            LOGGER.error("异常日志发送企业微信通知异常:{}", e.getMessage(), e);
        }
    }

}
